﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace FCSChart.Graphical
{
    /// <summary>
    /// 4象限门
    /// </summary>
    public class QuadrantGraphical : BaseGraphical
    {
        private double[] points;
        /// <summary>
        /// 4象限门5个点位
        /// </summary>
        public double[] Points
        {
            get { return points; }
            protected set { points = value; OnPropertyChanged("Points"); }
        }
        public QuadrantGraphical()
        {
            Points = new double[6];
            this.Fill = Brushes.Transparent;
        }
        public QuadrantGraphical(QuadrantGraphicalModel model) : base(model)
        {
            this.Points = new double[] { model.Left, model.Top, model.Right, model.Bottom, model.CenterX, model.CenterY };
            this.Fill = Brushes.Transparent;
            if (model.AreaNames != null && model.AreaNames.Count == 4)
            {
                Areas = model.AreaNames.Select(p => new GraphicalArea() { Name = p }).ToArray();
                Helper.AddExistedGraphicalName(this.ShortName, model.AreaNames.ToArray());
                if (model.AreaColors != null && model.AreaColors.Count == 4)
                {
                    Areas[0].DisplayColor = model.AreaColors[0];
                    Areas[1].DisplayColor = model.AreaColors[1];
                    Areas[2].DisplayColor = model.AreaColors[2];
                    Areas[3].DisplayColor = model.AreaColors[3];
                }
            }
        }
        protected override void InitName()
        {
            this.Name = "Quadrant";
            this.ShortName = "Q";
        }

        #region 4象限门不移动
        internal override void Move(double x, double y) { }
        #endregion

        #region 绘制
        internal override void Drawing()
        {
            if (OwnerChart == null || !OwnerChart.IsLoaded
                || OwnerChart.XAxis == null || !OwnerChart.XAxis.IsLoaded || OwnerChart.XAxis.ActualWidth == 0
                || OwnerChart.YAxis == null || !OwnerChart.YAxis.IsLoaded || OwnerChart.YAxis.ActualHeight == 0) return;
            if (GraphicalShape == null)
            {
                Binding binding = new Binding("Points") { Source = this, Converter = Converters.QuadrantDoublesToGeometryConverter.Converter, ConverterParameter = OwnerChart, Mode = BindingMode.OneWay };
                var temp = new Path() { Cursor = Cursors.Hand };
                temp.SetBinding(Path.DataProperty, binding);
                temp.SetBinding(Shape.FillProperty, new Binding("Fill") { Source = this });
                temp.SetBinding(Shape.StrokeProperty, new Binding("Stroke") { Source = this });
                temp.SetBinding(Shape.StrokeThicknessProperty, new Binding("StrokeThickness") { Source = this });
                temp.SetBinding(FrameworkElement.ContextMenuProperty, new Binding("ContextMenu") { Source = this });
                temp.Focusable = true;
                temp.MouseDown += Graphical_MouseDown;
                GraphicalShape = temp;
            }
            OnPropertyChanged("Points");
            if (!IsCreateing) DrawingControl();
        }

        internal override void PanelMouseDown(object sender, MouseButtonEventArgs e) { }

        internal override void PanelMouseMove(object sender, MouseEventArgs e)
        {
            if (IsCreateing && sender is Panel panel && Points.Length == 6)
            {
                var point = e.GetPosition(panel);
                var x = OwnerChart.XAxis.GetLocationValue(point.X);
                var y = OwnerChart.YAxis.GetLocationValue(point.Y);
                Points = new double[] { y, x, y, x, x, y };
            }
        }

        internal override void PanelMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (IsCreateing) IsCreateing = false;
        }
        #endregion

        #region 绘制门的控制按钮和区域
        protected GraphicalArea[] Areas { get; set; }
        protected override void DrawingControl()
        {
            var center = new Point(OwnerChart.XAxis.GetValueLocation(points[4]), OwnerChart.YAxis.GetValueLocation(points[5]));
            var left = new Point(5, OwnerChart.YAxis.GetValueLocation(points[0]));
            var top = new Point(OwnerChart.XAxis.GetValueLocation(points[1]), 5);
            var right = new Point(OwnerChart.XAxis.ActualWidth - 5, OwnerChart.YAxis.GetValueLocation(points[2]));
            var bottom = new Point(OwnerChart.XAxis.GetValueLocation(points[3]), OwnerChart.YAxis.ActualHeight - 5);
            if (Areas == null)
            {
                Areas = new GraphicalArea[] {
                    new GraphicalArea() { Name = OwnerChart.CreateNewAreaNameFunction(this) },
                    new GraphicalArea() { Name = OwnerChart.CreateNewAreaNameFunction(this) },
                    new GraphicalArea() { Name = OwnerChart.CreateNewAreaNameFunction(this) },
                    new GraphicalArea() { Name = OwnerChart.CreateNewAreaNameFunction(this) }
                };
            }
            this.OwnerChart.AddGraphicalArea(Areas[0], Areas[1], Areas[2], Areas[3]);
            if (ControlShapes.Count <= 0)
            {
                ControlShapes.Add(new Path() { Data = new EllipseGeometry(left, 5, 5), Cursor = Cursors.SizeNS });
                ControlShapes.Add(new Path() { Data = new EllipseGeometry(top, 5, 5), Cursor = Cursors.SizeWE });
                ControlShapes.Add(new Path() { Data = new EllipseGeometry(right, 5, 5), Cursor = Cursors.SizeNS });
                ControlShapes.Add(new Path() { Data = new EllipseGeometry(bottom, 5, 5), Cursor = Cursors.SizeWE });
                ControlShapes.Add(new Path() { Data = new EllipseGeometry(center, 5, 5), Cursor = Cursors.SizeAll });
            }
            else
            {
                if (ControlShapes[0] is Path p0 && p0.Data is EllipseGeometry e0) e0.Center = left;
                if (ControlShapes[1] is Path p1 && p1.Data is EllipseGeometry e1) e1.Center = top;
                if (ControlShapes[2] is Path p2 && p2.Data is EllipseGeometry e2) e2.Center = right;
                if (ControlShapes[3] is Path p3 && p3.Data is EllipseGeometry e3) e3.Center = bottom;
                if (ControlShapes[4] is Path p4 && p4.Data is EllipseGeometry e4) e4.Center = center;
            }
            Areas[0].Center = new Point(center.X / 2, center.Y / 2);
            Areas[1].Center = new Point((right.X + center.X) / 2, center.Y / 2);
            Areas[2].Center = new Point(center.X / 2, (bottom.Y + center.Y) / 2);
            Areas[3].Center = new Point((right.X + center.X) / 2, (bottom.Y + center.Y) / 2);
        }
        /// <summary>
        /// 移动门的控制按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void Shape_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed && sender is Path path && path.Data is EllipseGeometry ellipse)
            {
                var point = e.GetPosition(OwnerChart.ViewPanel);
                var index = ControlShapes.IndexOf(path);
                var value = Converters.AxisValueToViewValueConverter.Converter.ConvertBack(point, null, OwnerChart, null);
                if (value is Point p)
                {
                    switch (index)
                    {
                        case 0:
                            Points[0] = p.Y;
                            break;
                        case 1:
                            Points[1] = p.X;
                            break;
                        case 2:
                            Points[2] = p.Y;
                            break;
                        case 3:
                            Points[3] = p.X;
                            break;
                        case 4:
                            Points[4] = p.X;
                            Points[5] = p.Y;
                            break;
                        default:
                            break;
                    }
                    ellipse.Center = point;
                    OnPropertyChanged("Points");
                }
                e.Handled = true;
            }
        }
        protected override void Shape_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            DrawingControl();
            base.Shape_MouseLeftButtonUp(sender, e);
        }
        protected override void Shape_MouseLeave(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed) DrawingControl();
            base.Shape_MouseLeave(sender, e);
        }
        #endregion

        /// <summary>
        /// 刷新区域内数据
        /// </summary>
        internal override async void RefreshAreaSource(Func<object, double> xValueConverter, Func<object, double> yValueConverter)
        {
            if (Areas == null || Areas.Length != 4) return;
            if (!isCreateing && Points != null && Points.Length == 6 && OwnerChart != null && OwnerChart.XSource != null && OwnerChart.YSource != null && OwnerChart.XAxis != null && OwnerChart.YAxis != null)
            {
                var xSource = OwnerChart.XSource;
                var ySource = OwnerChart.YSource;
                var count = Math.Min(xSource.Count, ySource.Count);
                var parentIndexs = OwnerChart.Indexs;
                var pointdoubles = Points;
                Func<double, double> XAxisFunc = OwnerChart.XAxis.ValueToAxisValue;
                Func<double, double> YAxisFunc = OwnerChart.YAxis.ValueToAxisValue;

                var center = new Point(XAxisFunc(points[4]), YAxisFunc(points[5]));
                var left = new Point(OwnerChart.XAxis.MinAxis, YAxisFunc(points[0]));
                var top = new Point(XAxisFunc(points[1]), OwnerChart.YAxis.Max);
                var right = new Point(OwnerChart.XAxis.MaxAxis, YAxisFunc(points[2]));
                var bottom = new Point(XAxisFunc(points[3]), OwnerChart.YAxis.Min);

                var maxDegreeOfParallelism = OwnerChart.Series == null ? 4 : OwnerChart.Series.MaxDegreeOfParallelism;
                var array = await Task.Run(() =>
                 {
                     var a1 = left.Y - center.Y;
                     var b1 = center.X - left.X;
                     var c1 = left.X * center.Y - center.X * left.Y;
                     var a2 = top.Y - center.Y;
                     var b2 = center.X - top.X;
                     var c2 = top.X * center.Y - center.X * top.Y;
                     var a3 = right.Y - center.Y;
                     var b3 = center.X - right.X;
                     var c3 = right.X * center.Y - center.X * right.Y;
                     var a4 = bottom.Y - center.Y;
                     var b4 = center.X - bottom.X;
                     var c4 = bottom.X * center.Y - center.X * bottom.Y;
                     ConcurrentBag<int>[] indexsArray = new ConcurrentBag<int>[] { new ConcurrentBag<int>(), new ConcurrentBag<int>(), new ConcurrentBag<int>(), new ConcurrentBag<int>() };
                     if (parentIndexs == null)
                     {
                         var result = Parallel.For(0, count, new ParallelOptions() { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (i) =>
                         {
                             PointInArea(i, indexsArray, left.Y, right.Y, top.X, bottom.X, xSource, ySource, xValueConverter, yValueConverter, XAxisFunc, YAxisFunc, a1, b1, c1, a2, b2, c2, a3, b3, c3, a4, b4, c4);
                         });
                     }
                     else
                     {
                         var result = Parallel.ForEach(parentIndexs, new ParallelOptions() { MaxDegreeOfParallelism = maxDegreeOfParallelism }, (i) =>
                         {
                             PointInArea(i, indexsArray, left.Y, right.Y, top.X, bottom.X, xSource, ySource, xValueConverter, yValueConverter, XAxisFunc, YAxisFunc, a1, b1, c1, a2, b2, c2, a3, b3, c3, a4, b4, c4);
                         });
                     }
                     return indexsArray;
                 });
                if (Areas == null || Areas.Length != 4) return;
                this.Areas[0].InsideIndexs = array[0].ToArray();
                this.Areas[1].InsideIndexs = array[1].ToArray();
                this.Areas[2].InsideIndexs = array[2].ToArray();
                this.Areas[3].InsideIndexs = array[3].ToArray();
            }
            else
            {
                foreach (var area in Areas)
                {
                    area.InsideIndexs = null;
                }
            }
        }

        private void PointInArea(int i, ConcurrentBag<int>[] indexsArray, double leftY, double rightY, double topX, double bottomX, IList xSource, IList ySource, Func<object, double> xValueConverter, Func<object, double> yValueConverter, Func<double, double> xAxisFunc, Func<double, double> yAxisFunc, double a1, double b1, double c1, double a2, double b2, double c2, double a3, double b3, double c3, double a4, double b4, double c4)
        {
            var x = xAxisFunc(xValueConverter == null ? Convert.ToDouble(xSource[i]) : xValueConverter(xSource[i]));
            var y = yAxisFunc(yValueConverter == null ? Convert.ToDouble(ySource[i]) : yValueConverter(ySource[i]));
            var d1 = a1 * x + b1 * y + c1;
            if (d1 == 0) indexsArray[0].Add(i);//在中心点到左侧点的线上，算到第一象限里面
            else if (d1 > 0)//在中心点到左侧点的线的右侧
            {
                var d2 = a2 * x + b2 * y + c2;
                if (d2 == 0) indexsArray[1].Add(i);
                else if (d2 > 0)
                {
                    var d3 = a3 * x + b3 * y + c3;
                    if (d3 == 0) indexsArray[3].Add(i);
                    else if (d3 > 0)
                    {
                        var d4 = a4 * x + b4 * y + c4;
                        if (d4 == 0) indexsArray[2].Add(i);
                        else if (d4 < 0) indexsArray[3].Add(i);
                        else PointInArea(i, indexsArray, x, y, leftY, rightY, topX, bottomX);
                    }
                    else indexsArray[1].Add(i);
                }
                else indexsArray[0].Add(i);
            }
            else//在中心点到左侧点的线的左侧
            {
                var d4 = a4 * x + b4 * y + c4;
                if (d4 >= 0) indexsArray[2].Add(i);
                else
                {
                    var d3 = a3 * x + b3 * y + c3;
                    if (d3 >= 0) indexsArray[3].Add(i);
                    else
                    {
                        var d2 = a2 * x + b2 * y + c2;
                        if (d2 >= 0) indexsArray[1].Add(i);
                        else PointInArea(i, indexsArray, x, y, leftY, rightY, topX, bottomX);
                    }
                }
            }
        }

        private void PointInArea(int i, ConcurrentBag<int>[] indexsArray, double x, double y, double leftY, double rightY, double topX, double bottomX)
        {
            if (x < topX && y > leftY) indexsArray[0].Add(i);
            else if (x > topX && y > rightY) indexsArray[1].Add(i);
            else if (x < bottomX && y < leftY) indexsArray[2].Add(i);
            else if (x > bottomX && y < rightY) indexsArray[3].Add(i);
        }

        protected override BaseGraphicalModel GetGraphicalMode()
        {
            if (Points == null) return null;
            return new QuadrantGraphicalModel()
            {
                AreaNames = Areas.Select(p => p.Name).ToArray(),
                AreaColors = this.Areas.Select(p => p.DisplayColor).ToArray(),
                Left = Points[0],
                Top = Points[1],
                Right = Points[2],
                Bottom = Points[3],
                CenterX = Points[4],
                CenterY = Points[5]
            };
        }
        public override void Dispose()
        {
            base.Dispose();
            if (Areas != null)
            {
                if (this.OwnerChart != null)
                    this.OwnerChart.RemoveGraphicalArea(Areas);
                Areas = null;
            }
        }
    }

    public class QuadrantGraphicalModel : BaseGraphicalModel
    {
        public double Left { get; set; }
        public double Top { get; set; }
        public double Right { get; set; }
        public double Bottom { get; set; }
        public double CenterX { get; set; }
        public double CenterY { get; set; }
    }
}
